package com.seed.bpmn.provider.activiti.service;

import com.seed.bpmn.engine.IBPMNRuntimeService;
import com.seed.bpmn.engine.common.constants.BpmnConstants;
import com.seed.bpmn.engine.common.util.Empty;
import com.seed.bpmn.engine.dto.*;
import com.seed.bpmn.provider.activiti.common.cmd.ScheduleCmd;
import com.seed.bpmn.provider.activiti.common.util.ActivitiUtil;
import com.seed.bpmn.provider.activiti.common.util.DTOUtil;
import net.sf.json.JSONObject;
import org.activiti.engine.*;
import org.activiti.engine.impl.RepositoryServiceImpl;
import org.activiti.engine.impl.bpmn.parser.BpmnParse;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntity;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.IdentityLink;
import org.activiti.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.seed.bpmn.engine.common.constants.BpmnConstants.ATTRIBUTE_TASK_MULTIINSTANCE;

/**
 * 流程流转时相关服务接口
 * @author cscz89@126.com
 */
@Component("bpmnRuntimeService")
public class BPMNRuntimeServiceImpl implements IBPMNRuntimeService {

    @Autowired
    private TaskService taskService;

    @Autowired
    private IdentityService identityService;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private ManagementService managementService;

    @Autowired
    private ActivitiUtil activitiUtil;

    @Override
    public ProcessInstanceStartDTO start(String processDefinitionId, String businessKey, String userId, Map<String, Object> variables, boolean autoDraft) throws Exception {

        if(Empty.isNotEmpty(businessKey) && Empty.isNotEmpty(processDefinitionId)){
            //用来设置启动流程的人员ID，引擎会自动把用户ID保存到activiti:initiator中
            identityService.setAuthenticatedUserId(userId);
            ProcessInstance pi = runtimeService.startProcessInstanceById(processDefinitionId, businessKey,variables);
            identityService.setAuthenticatedUserId(null);

            //获取第一个任务节点，按规范应该是拟稿节点
            ExecutionEntity entity = (ExecutionEntity) pi;

            String taskId = null;
            String taskDefKey = null;

            if(!Empty.isEmpty(entity.getTasks())){
                TaskEntity t = entity.getTasks().get(0);
                taskId = t.getId();
                taskDefKey = t.getTaskDefinitionKey();
            }

            //流程定义实体类,能得到流程的环节
            RepositoryServiceImpl repositoryServiceImpl = (RepositoryServiceImpl) this.repositoryService;
            ProcessDefinitionEntity pde = (ProcessDefinitionEntity) repositoryServiceImpl.getDeployedProcessDefinition(processDefinitionId);

            //通过流程环节id，可以得到流程环节
            ActivityImpl activity = pde.findActivity(taskDefKey);

            String dd = (String) activity.getProperty(BpmnConstants.ELEMENT_DOCUMENTATION);

            JSONObject documentation = new JSONObject().fromObject(dd);

            //获取下一节点类型
            String typeN = (String) documentation.get(BpmnConstants.ATTRIBUTE_TYPE);

            //拟稿人
            String initiator = null;

            //拟稿节点
            if(BpmnConstants.ATTRIBUTE_TASK_DRAFT.equals(typeN)){
                //拟稿人变量名
                String ivn = (String) pde.getProperty(BpmnParse.PROPERTYNAME_INITIATOR_VARIABLE_NAME);
                //拟稿人变量名
                initiator = (String) this.runtimeService.getVariable(pi.getId(), ivn);
            }

            this.taskService.claim(taskId, initiator);

            //流程实例创建成功后对象封装
            ProcessInstanceStartDTO sdto = new ProcessInstanceStartDTO();
            sdto.setTaskId(taskId);

            //是否需要自动完成拟稿节点
            if(autoDraft){
                //完成拟稿节点
                List<TaskEntityDTO> ts = this.complete(taskId, initiator, variables, false);
                sdto.setTasks(ts);
            }

            ProcessInstanceDTO pdto = DTOUtil.getProcessInstanceDTO(pi);
            sdto.setProcessInstance(pdto);

            return sdto;
        }else{
            return null;
        }
    }

    @Override
    public List<TaskEntityDTO> complete(String taskId, String userId, List<String> candidates, Map<String,Boolean> flows, String suggestion) throws Exception {
        Map<String, Object> variables = new HashMap<String,Object>();
        variables.put(BpmnConstants.ATTRIBUTE_TASK_SUGGESTION, suggestion);
        variables.put(BpmnConstants.ATTRIBUTE_TASK_USER_CANDIDATEUSERS, candidates);
        variables.put(BpmnConstants.ATTRIBUTE_TASK_USER_CANDIDATEGROUPS, new ArrayList<String>());
        variables.putAll(flows);
        return this.complete(taskId,userId,variables,false);
    }

    @Override
    public List<TaskEntityDTO> complete(String taskId, String userId, Map<String, Object> variables, boolean localScope) throws Exception {
        Task t = taskService.createTaskQuery().taskId(taskId).singleResult();

        //保存意见
        String suggestion = (String) variables.get(BpmnConstants.ATTRIBUTE_TASK_SUGGESTION);
        this.addComment(taskId,userId,t.getProcessInstanceId(),suggestion);
        variables.remove(BpmnConstants.ATTRIBUTE_TASK_SUGGESTION);

        //过滤任务完成参数
        initCompleteVariables(t,variables);

        String processInstanceId = t.getProcessInstanceId();

        //任务处理前其所在流程实例待办任务列表
        List<Task> olist = taskService.createTaskQuery().processInstanceId(processInstanceId).list();

        //完成任务
        identityService.setAuthenticatedUserId(userId);
        taskService.complete(taskId, variables, localScope);
        identityService.setAuthenticatedUserId(null);

        //任务处理后其所在流程实例待办任务列表
        List<Task> nlist = taskService.createTaskQuery().processInstanceId(processInstanceId).list();

        //返回数据
        List<TaskEntityDTO> dlist = new ArrayList<TaskEntityDTO>();
        for(Task nt : nlist){
            boolean flag = true;

            for(Task ot : olist){
                if(nt.getId().equals(ot.getId())){
                    flag = false;
                    break;
                }
            }

            if(flag){
                dlist.add(DTOUtil.getTaskEntityDTO(nt, this.findTaskIdentityLink(nt.getId())));
            }
        }

        return dlist;
    }

    public List<IdentityLinkDTO> findTaskIdentityLink(String taskId) throws Exception {
        List<IdentityLinkDTO> list = new ArrayList<>();

        List<IdentityLink> ilist = taskService.getIdentityLinksForTask(taskId);
        for(IdentityLink i : ilist){
            IdentityLinkDTO idto = DTOUtil.getIdentityLinkDTO(i);
            if(Empty.isNotEmpty(idto)){
                list.add(idto);
            }
        }
        return list;
    }

    @Override
    public boolean sign(String taskId, String userId, boolean sign) throws Exception {
        if(Empty.isEmpty(taskId)){
            return false;
        }else{
            Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
            boolean flag = false;
            if(Empty.isNotEmpty(task)){
                boolean isAssignee = Empty.isNotEmpty(task.getAssignee());
                if(sign && !isAssignee){
                    //判断任务是否被签收，如果已经签收过，就不能进行签收
                    taskService.claim(taskId, userId);
                    flag = true;
                }else if(!sign && isAssignee){
                    //判断任务是否被签收，如果未签收过，就没必要进行取消签收操作
                    taskService.unclaim(taskId);
                    flag = true;
                }
            }
            return flag;
        }
    }

    @Override
    public List<TaskDTO> findTaskByProcessInstanceId(String processInstanceId) throws Exception {
        List<Task> tlist = taskService.createTaskQuery().processInstanceId(processInstanceId).list();

        List<TaskDTO> list = new ArrayList<TaskDTO>();
        for(Task t : tlist){
            list.add(DTOUtil.getTaskDTO(t));
        }

        return list;
    }

    @Override
    public List<TaskDTO> findTaskByProcessInstanceId(String processInstanceId, String userId) throws Exception {
        List<Task> tlist = taskService.createTaskQuery().processInstanceId(processInstanceId).taskCandidateOrAssigned(userId).list();

        List<TaskDTO> list = new ArrayList<TaskDTO>();
        for(Task t : tlist){
            list.add(DTOUtil.getTaskDTO(t));
        }

        return list;
    }

    @Override
    public CommentDTO addComment(String taskId, String userId, String processInstanceId, String suggestion) throws Exception {
        CommentDTO dto = null;
        if(!Empty.isEmpty(suggestion)){
            identityService.setAuthenticatedUserId(userId);
            Comment c = this.taskService.addComment(taskId, processInstanceId, suggestion);
            identityService.setAuthenticatedUserId(null);

            dto = DTOUtil.getCommentDTO(c);
        }

        return dto;
    }

    public void initCompleteVariables(Task task,Map<String, Object> variables) throws Exception {
        RepositoryServiceImpl repositoryServiceImpl = (RepositoryServiceImpl) this.repositoryService;

        //流程定义实体类,能得到流程的环节
        ProcessDefinitionEntity pde = (ProcessDefinitionEntity) repositoryServiceImpl.getDeployedProcessDefinition(task.getProcessDefinitionId());

        //通过流程环节id，可以得到流程环节
        ActivityImpl activityImpl = pde.findActivity(task.getTaskDefinitionKey());

        String multiInstance = (String)activityImpl.getProperty(ATTRIBUTE_TASK_MULTIINSTANCE);

        if(Empty.isNotEmpty(multiInstance)){
            //多实例,非最后一个实例，移除任务完成参数中处理人、处理组
            boolean islast = activitiUtil.multiInstLast(task.getId());
            if(!islast){
                variables.remove(BpmnConstants.ATTRIBUTE_TASK_USER_CANDIDATEUSERS);
                variables.remove(BpmnConstants.ATTRIBUTE_TASK_USER_CANDIDATEGROUPS);
            }
        }
    }

    @Override
    public ScheduleDTO scheduleProcess(String processInstanceId, String nodeId, List<String> candidates, String reason) throws Exception{
        ScheduleDTO s = new ScheduleDTO();
        if(Empty.isNotEmpty(processInstanceId)){
            Map<String,Object> vs = new HashMap<String,Object>();
            vs.put(BpmnConstants.ATTRIBUTE_TASK_USER_CANDIDATEGROUPS,new ArrayList<>());
            vs.put(BpmnConstants.ATTRIBUTE_TASK_USER_CANDIDATEUSERS,candidates);

            ScheduleCmd command = new ScheduleCmd(processInstanceId,nodeId,vs);
            if(Empty.isNotEmpty(reason)){
                command.setReason(reason);
            }

            s = managementService.executeCommand(command);

            //获取调度后最新待办任务
            List<Task> nlist = taskService.createTaskQuery().processInstanceId(processInstanceId).list();
            for(Task t : nlist){
                List<IdentityLinkDTO> ilist = this.findTaskIdentityLink(t.getId());
                TaskEntityDTO dto = DTOUtil.getTaskEntityDTO(t, ilist);
                s.getNewTasks().add(dto);
            }
        }else{
            s.setMessage("抱歉，调度失败！流程实例ID不可为空！");
            s.setSuccess(false);
        }
        return s;
    }

    @Override
    public boolean deleteProcessByInstanceId(String processInstanceId, String reason) throws Exception {
        return false;
    }

}
